#ifdef DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "block.h"

#define cGarbage		0xCC

#define bPtrLess(pLeft,pRight)			((pLeft)<(pRight))
#define bPtrGrtr(pLeft,pRight)			((pLeft)>(pRight))
#define bPtrEqual(pLeft,pRight)			((pLeft)==(pRight))
#define bPtrLessEq(pLeft,pRight)		((pLeft)<=(pRight))
#define bPtrGrtrEq(pLeft,pRight)		((pLeft)>=(pRight))

static blockinfo *pbiHead = NULL;

/* SUCC or ASSERT ERROR */
static blockinfo * pbiGetBlockInfo(char *pc)
{
    blockinfo *pbi;
    for (pbi = pbiHead; pbi != NULL; pbi = pbi->pbiNext)
    {
        char *pcStart = pbi->pc; 
        char *pcEnd = pbi->pc + pbi->size - 1;
        if (bPtrGrtrEq(pc, pcStart) && bPtrLessEq(pc, pcEnd))
            break;
    }

    ASSERT(pbi != NULL);
    return pbi;
}

/* return TRUE or FALSE */
bool bCreateBlockInfo(char *pcNew, size_t sizeNew)
{
    blockinfo *pbi;
    ASSERT(pcNew != NULL && sizeNew != 0);

    pbi = (blockinfo *)malloc(sizeof(blockinfo));
    if (pbi != NULL)
    {
        pbi->pc = pcNew;
        pbi->size = sizeNew;
        pbi->pbiNext = pbiHead;
        pbiHead = pbi;
    }

    return (pbi != NULL);
}

void FreeBlockInfo(char *pcToFree)
{
    blockinfo *pbi, *pbiPrev;
    for (pbi=pbiHead, pbiPrev=NULL; pbi != NULL; pbi = pbi->pbiNext)
    {
        if (bPtrEqual(pbi->pc, pcToFree))
        {
            if (pbiPrev == NULL)
                pbiHead = pbi->pbiNext;
            else
                pbiPrev->pbiNext = pbi->pbiNext;
            break;
        }
        pbiPrev = pbi;
    }

    ASSERT(pbi != NULL);
    memset(pbi, cGarbage, sizeof(blockinfo));
    free(pbi);
}

void UpdateBlockInfo(char *pcOld, char *pcNew, size_t sizeNew)
{
    blockinfo *pbi;
    ASSERT(pcNew != NULL && sizeNew != 0);
    pbi = pbiGetBlockInfo(pcOld);
    ASSERT(pcOld == pbi->pc);
    pbi->pc = pcNew;
    pbi->size = sizeNew;
}

size_t sizeofBlock(char *pc)
{
    blockinfo *pbi;
    pbi = pbiGetBlockInfo(pc);
    ASSERT(pc == pbi->pc);
    return pbi->size;
}


void ClearMemoryRefs(void)
{
    blockinfo *pbi;
    for (pbi = pbiHead; pbi != NULL; pbi = pbi->pbiNext)
        pbi->bReferenced = false;        
}

void NoteMemoryRef(void *pv)
{
    blockinfo *pbi;
    pbi = pbiGetBlockInfo((char *)pv);
    pbi->bReferenced = true;
}

void CheckMemoryRefs(void)
{
    blockinfo *pbi;
    for (pbi = pbiHead; pbi != NULL; pbi = pbi->pbiNext)
    {
        ASSERT(pbi->pc != NULL && pbi->size != 0);
        ASSERT(pbi->bReferenced);
    }
}

/* TRUE or ASSERT ERROR */
bool bValidPointer(void *pv, size_t size)
{
    blockinfo *pbi;
    char *pc = (char *)pv;
    pbi = pbiGetBlockInfo(pc);
    ASSERT(pv != NULL && size != 0);
    ASSERT(bPtrLessEq(pc+size, pbi->pc+pbi->size));
    return true;
}


#endif
